import inspect
from datetime import datetime

class DirectException(Exception):
    pass




try:
    import json
except ImportError:
    try:
        import simplejson as json
    except ImportError:
        raise DirectException("No json library avilable."
        "Please install simplejson or upgrade to python 2.6.")
        
def __json_default__(o):
    if isinstance(o, set):
        l=list(o)
        return json.dumps(l,default=__json_default__)
    if isinstance(o, datetime):
        return o.strftime('%Y%m%d%H%M%S')
    try:
        getattr(o, '__json__')
        has_json =True
    except AttributeError:
        has_json=False
    if has_json :
        return o.__json__()
    else:
        raise TypeError('%r is not JSON serializable' % o)
        
class DirectRouter(object):
    
    def __init__(self,real_object):
        self.proxy=real_object
    def __call__(self,env,start_response):
        status = '200 OK' # HTTP Status  
        headers = [('Content-type', 'application/json')] # HTTP Headers 
        
        try:
            len =int(env.get('CONTENT_LENGTH',0))
            input=env.get('wsgi.input',None)
            
            body=input.read(len)
            result=self._call(body)
            if isinstance(result, unicode):
                result=result.encode('utf-8')
        except DirectException,e:
            raise e
        start_response(status, headers) 
        return result
    
    def _call(self,body):
        
        body = json.loads(body)
        self._body = body
        
        if isinstance(body,list):
            directRequests = body
        elif isinstance(body,dict):
            directRequests = [body]
        else:
            raise DirectException("Body is not a support type:%s"%body)
        
        responses=[]
        
        for req in directRequests:
            responses.append(self._doRequest(req))
            
        if len(responses) == 1:
            responses = responses[0]
            
        return json.dumps(responses,default=__json_default__)
    
    def _doRequest(self,request):
        action = request.get('action')
        clsname =self.proxy.__class__.__name__
        
        if action != clsname:
            raise DirectException("Action specified in request ('%s') is"
                                  "not named %s"%(action,clsname)
                                  )
            
        method = request.get('method')
        if not method:
            raise DirectException("No method specified.Is this a valid"
                                  "Ext.Direct request?"
                                  )
            
        try:
            _targetfn=getattr(self.proxy,method)
        except AttributeError:
            raise DirectException("%s is not the name of a method on %s"%(method,clsname))
            
        data=request.get('data')
        if not data:
            data={}
            
        if isinstance(data,list):
            self._data=data
            result = _targetfn(*data)
        else:
            #data=data[0]
            data=dict( (str(k),v) for k,v in data.iteritems() )
            self._data=data
            result = _targetfn(**data)
        
        return {
                'type':'rpc',
                'tid':request['tid'],
                'action':action,
                'method':method,
                'result':result
                }
        
        
class DirectProviderDefinition(object):
    
    def __init__(self,routercls,url,ns=None,timeout=None):
        if isinstance(routercls, type):
            self.routercls = routercls
        else:
            self.routercls=routercls.__class__
        self.url=url
        self.ns=ns
        self.timeout=timeout
    
    def __call__(self,env,start_response):
        status = '200 OK' # HTTP Status  
        headers = [('Content-type', 'text/javascript')] # HTTP Headers  
        start_response(status, headers) 
        return self.render()
        
    def _config(self):
        actions = []
        for name,value in inspect.getmembers(self.routercls):
            if name.startswith("_"):
                continue
            if inspect.ismethod(value):
                
                args= [v for v in inspect.getargspec(value)[0] if v!='self']
                arglen = len(args)
                arglen = arglen if arglen >=1 else 0
                method={'name':name,'len':arglen}
                try:
                    getattr(value, '__form_handler__')
                    is_form_handler = True
                except AttributeError:
                    is_form_handler =False
                if is_form_handler == True:
                    method['formHandler']=True
                actions.append(method)

        config={
                'id':self.routercls.__name__,
                'type':'remoting',
                'url':self.url,
                'actions':{self.routercls.__name__:actions}
                }
          
        if self.timeout:
            config['timeout'] = self.timeout
        if self.ns:
            config['namespace'] = self.ns
        return config
    
    def render(self):
        config = self._config()
        source = "Ext.Direct.addProvider(%s);"%json.dumps(config)
        return source.strip()